#include "balancer.h"
#include "submission.h"
#include <sys/signal.h>
#include <sys/wait.h>
#include <cstdlib>

AppBalancer* AppBalancer::sInstance = NULL;

AppBalancer* AppBalancer::createInstance(string pConfigPath) {
	if (sInstance == NULL) {
		sInstance = new AppBalancer(pConfigPath);
	}
	
	return sInstance;
}

AppBalancer* AppBalancer::getInstance() {
	return sInstance;
}

void AppBalancer::registerFailedSubmission(string pSubmissionId) {
	mLogFile << getCurrentDate() << " Subs#" << pSubmissionId << " failed. Requeuing" << endl;
	mSubmissionQueue.push(pSubmissionId);
}

AppBalancer::AppBalancer(string pPath , int nWorker , string pLogFilePath , int pChannelId , int pMessageType) {
	mReady = false;
	
	mGraderPath = pPath;
	this->nWorker = nWorker;
	mLogFile.open(pLogFilePath.c_str() , ios_base::app);
	mChannelId = pChannelId;
	mMessageType = pMessageType;
	
	cout << mGraderPath << " " << this->nWorker << " " << mLogFile.is_open() << " " << mChannelId << " " << mMessageType << endl;
	
	init();
}

AppBalancer::AppBalancer(string pConfigPath) {
	ifstream config(pConfigPath.c_str());
	mReady = false;
	
	if (config.is_open()) {
		string tLogPath;
		
		config >> mGraderPath;
		config >> nWorker;
		config >> tLogPath;
		mLogFile.open(tLogPath.c_str() , ios_base::app);
		config >> mChannelId;
		config >> mMessageType;
		
		///Read urls
		string tUrl;
		while (config >> tUrl) {
			mUrl.push_back(tUrl);
		}
		
		signal(SIGCHLD , AppBalancer::sighandler);
		signal(SIGINT , AppBalancer::sighandler);
		
		init();
	}
	else {
		cerr << "Unable to read config file, exiting..." << endl;
	}
}

string AppBalancer::getGraderPath() {
	return mGraderPath;
}

void AppBalancer::prepare() {
	for (int i = 0 ; i < nWorker ; i++) {
		//pid_t pid = fork();
		
		/*if (pid == 0) {
			char sChannel[100], sMsgType[100];
			
			sprintf(sChannel , "%d" , mChannelId);
			sprintf(sMsgType , "%d" , mMessageType + i + 1);
			
			execl(mGraderPath.c_str() , "yiic" , "evaluate" , "queue" , sChannel , sMsgType , (char*) 0);
			exit(0);
		}
		else */{
			NewWorker* tWorker = new NewWorker(mChannel , &mLogFile , mMessageType + i + 1 , 0);
			tWorker->start();
			mWorkers.push_back(tWorker);
		}
	}
}

void AppBalancer::init() {
	mReady = true;
	mChannel = new MessageChannel(mChannelId);
}

void AppBalancer::sighandler(int signal) {
	if (signal == SIGCHLD) {
		int stat;
		pid_t pid;		
		pid = waitpid(-1 , &stat , 0);
		
		AppBalancer::getInstance()->mLogFile << getCurrentDate() << " child #" << pid << " has just died. Something wrong? " << endl;
	}
	else if (signal == SIGINT) {
		cout << "INTERRUPTED" << endl;
		for (int i = 0 ; i < AppBalancer::getInstance()->nWorker ; i++) {
			delete AppBalancer::getInstance()->mWorkers[i];
		}
		
		exit(0);
	}
}

AppBalancer::~AppBalancer() {
	/*if (mChannel != NULL) 
		delete mChannel;
	
	for (int i = 0 ; i < nWorker ; i++) {
		if (mWorkers[i] != NULL) 
			delete mWorkers[i];
	}*/
}

void AppBalancer::run() {
	if (!mReady) {
		return;
	}
	prepare();
	
	cout << "PID : " << getpid() << endl;
	mLogFile << getCurrentDate() << " Starting balancer with pid: " << getpid() << endl;
	
	//SubmissionMessage *message = new SubmissionMessage(mMessageType);
	
	while (1) {
		if (mSubmissionQueue.size() > 0) {
			string tSubmissionId = mSubmissionQueue.front();
			mSubmissionQueue.pop();
			
			mLogFile << getCurrentDate() << " Subs#" << tSubmissionId << " received" << endl;
			bool found = false;
			int foundWorker;
			
			while (!found) {
				for (int i = 0 ; i < nWorker && !found ; i++) {
					if (!mWorkers[i]->isBusy()) {
						found = true;
						foundWorker = i;
					}
				}
			}
 			
			///Dispatch
			cout << "Dispatching subs#" << tSubmissionId << " to worker #" << foundWorker << " by : " << getpid() << endl;
			
			mLogFile << getCurrentDate() << " Subs#" << tSubmissionId << " dispatched to worker #" << foundWorker << " by : " << getpid() << endl;
			mWorkers[foundWorker]->dispatch(tSubmissionId);
		}
		else {
			usleep(200);
		}
	}
	
	/*while(1) {
		if (mChannel->receive(message , message->getLength()) >= 0) { ///GOT
			mLogFile << getCurrentDate() << " Subs#" << message->getSubmissionId() << " received" << endl;
			bool found = false;
			int foundWorker;
			
			while (!found) {
				for (int i = 0 ; i < nWorker && !found ; i++) {
					if (!mWorkers[i]->isBusy()) {
						found = true;
						foundWorker = i;
					}
				}
			}
 			
			///Dispatch
			cout << "Dispatching subs#" << message->getSubmissionId() << " to worker #" << foundWorker << " by : " << getpid() << endl;
			
			mLogFile << getCurrentDate() << " Subs#" << message->getSubmissionId() << " dispatched to worker #" << foundWorker << " by : " << getpid() << endl;
			mWorkers[foundWorker]->dispatch(message->getSubmissionId());
		}
	}*/
}

void AppBalancer::setup() {
}

void AppBalancer::execute() {
	SubmissionMessage *message = new SubmissionMessage(mMessageType);
	
	while(1) {
		if (mChannel->receive(message , message->getLength()) >= 0) { ///GOT
			mSubmissionQueue.push(message->getSubmissionId());
		}
	}
	
	delete message;
}
